/*
 * This file is part of aion-engine <aion-engine.com>
 *
 * aion-engine is private software: you can redistribute it and or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Private Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aion-engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser Public License
 * along with aion-engine.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionengine.gameserver.controllers;

import com.aionengine.gameserver.controllers.observer.ActionObserver;
import com.aionengine.gameserver.controllers.observer.ObserverType;
import com.aionengine.gameserver.dataholders.DataManager;
import com.aionengine.gameserver.model.TeleportAnimation;
import com.aionengine.gameserver.model.gameobjects.HouseObject;
import com.aionengine.gameserver.model.gameobjects.Npc;
import com.aionengine.gameserver.model.gameobjects.VisibleObject;
import com.aionengine.gameserver.model.gameobjects.player.Player;
import com.aionengine.gameserver.model.house.House;
import com.aionengine.gameserver.model.templates.housing.HouseType;
import com.aionengine.gameserver.model.templates.zone.ZoneInfo;
import com.aionengine.gameserver.network.aion.AionServerPacket;
import com.aionengine.gameserver.network.aion.serverpackets.SM_DELETE_HOUSE;
import com.aionengine.gameserver.network.aion.serverpackets.SM_HOUSE_RENDER;
import com.aionengine.gameserver.network.aion.serverpackets.SM_HOUSE_UPDATE;
import com.aionengine.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionengine.gameserver.services.teleport.TeleportService2;
import com.aionengine.gameserver.utils.MathUtil;
import com.aionengine.gameserver.utils.PacketSendUtility;
import com.aionengine.gameserver.utils.ThreadPoolManager;
import com.aionengine.gameserver.world.World;
import javolution.util.FastMap;

import java.util.List;

/**
 * @author Rolandas
 */
public class HouseController extends VisibleObjectController<House> {

    FastMap<Integer, ActionObserver> observed = new FastMap<Integer, ActionObserver>().shared();

    @Override
    public void see(VisibleObject object) {
        Player p = (Player) object;
        ActionObserver observer = new ActionObserver(ObserverType.MOVE);
        p.getObserveController().addObserver(observer);
        observed.put(p.getObjectId(), observer);
        AionServerPacket packet;
        if (getOwner().isInInstance())
            packet = new SM_HOUSE_UPDATE(getOwner());
        else
            packet = new SM_HOUSE_RENDER(getOwner());
        PacketSendUtility.sendPacket(p, packet);

        spawnObjects();
    }

    @Override
    public void notSee(VisibleObject object, boolean isOutOfRange) {
        Player p = (Player) object;
        ActionObserver observer = observed.remove(p.getObjectId());
        if (isOutOfRange) {
            observer.moved();
            if (!getOwner().isInInstance())
                PacketSendUtility.sendPacket(p, new SM_DELETE_HOUSE(getOwner().getAddress().getId()));
        }
        p.getObserveController().removeObserver(observer);
    }

    public void spawnObjects() {
        if (getOwner().getRegistry() != null) {
            for (HouseObject<?> obj : getOwner().getRegistry().getSpawnedObjects()) {
                obj.spawn();
            }
        }
    }

    /**
     * Used for owner player only
     */
    public void updateAppearance() {
        ThreadPoolManager.getInstance().execute(new Runnable() {

            @Override
            public void run() {
                for (int playerId : observed.keySet()) {
                    Player player = World.getInstance().findPlayer(playerId);
                    if (player == null)
                        continue;
                    PacketSendUtility.sendPacket(player, new SM_HOUSE_UPDATE(getOwner()));
                }
            }
        });
    }

    public void broadcastAppearance() {
        ThreadPoolManager.getInstance().execute(new Runnable() {

            @Override
            public void run() {
                for (int playerId : observed.keySet()) {
                    Player player = World.getInstance().findPlayer(playerId);
                    if (player == null)
                        continue;
                    PacketSendUtility.sendPacket(player, new SM_HOUSE_RENDER(getOwner()));
                }
            }
        });
    }

    public void kickVisitors(Player kicker, boolean kickFriends, boolean onSettingsChange) {
        List<ZoneInfo> zoneInfo = DataManager.ZONE_DATA.getZones().get(getOwner().getWorldId());
        for (ZoneInfo info : zoneInfo) {
            if (info.getZoneTemplate().getName().name().equals(getOwner().getName())) {
                for (Integer objId : this.observed.keySet()) {
                    if (objId == getOwner().getOwnerId())
                        continue;
                    if (!kickFriends && kicker != null && kicker.getFriendList().getFriend(objId) != null)
                        continue;
                    Player visitor = World.getInstance().findPlayer(objId);
                    if (visitor != null) {
                        if (visitor.isInsideZone(info.getZoneTemplate().getName())) {
                            moveOutside(visitor, onSettingsChange);
                        }
                    }
                }
                break;
            }
        }
        if (kicker != null) {
            if (!kickFriends) {
                PacketSendUtility.sendPacket(kicker, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_ORDER_OUT_WITHOUT_FRIENDS);
            } else {
                PacketSendUtility.sendPacket(kicker, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_ORDER_OUT_ALL);
            }
        }
    }

    private void moveOutside(Player player, boolean onSettingsChange) {
        if (getOwner().getHouseType() == HouseType.STUDIO) {
            float x = getOwner().getAddress().getExitX();
            float y = getOwner().getAddress().getExitY();
            float z = getOwner().getAddress().getExitZ();
            TeleportService2.teleportTo(player, getOwner().getAddress().getExitMapId(), 1, x, y, z, player.getHeading(), TeleportAnimation.BEAM_ANIMATION);
        } else {
            Npc sign = getOwner().getCurrentSign();
            double radian = Math.toRadians(MathUtil.convertHeadingToDegree(sign.getHeading()));
            float x = (float) (sign.getX() + (8 * Math.cos(radian)));
            float y = (float) (sign.getY() + (8 * Math.sin(radian)));
            TeleportService2.teleportTo(player, getOwner().getWorldId(), 1, x, y, player.getZ() + 1, player.getHeading(),
                    TeleportAnimation.BEAM_ANIMATION);
        }
        if (onSettingsChange)
            PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CHANGE_OWNER);
        else
            PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_REQUEST_OUT);
    }
}
